<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <title>依赖收集原理</title>
</head>
<body>
<div id="app"></div>
<!-- <script>
  // 1. 为什么要用Reflect？
  const person = {
    name: "xm",
    get aliasName() {
      // 属性访问器
      return "handSome" + this.name;
    },
  };
  const proxy = new Proxy(person, {
    get(target, key, receiver) {
      // receiver:代理对象
      console.log(key);
      // return target[key]  // this = person
      return Reflect.get(target, key, receiver); // this = receiver   (receiver[key) // person.name 不会触发get
    },
    set(target, key, value, receiver) {
      // target[key] = value
      Reflect.set(target, key, value, receiver);
      return true;
    },
  });

  // 一段伪代码
  // effect(() => {
  //     console.log(proxy.aliasName)
  // })
  proxy.name = "xh";
  // 此时我们修改了name，effect 内的 proxy.aliasName是不会触发更新的，但是proxy.aliasName是依赖name的，所以就会有问题

  // 采用Reflect后，如果访问aliasName，紧接着会去取name，此时是在 receiver （代理对象）上取值，会再触发get方法
  console.log(proxy.aliasName);
  // 使用 target[key]， 不会打印name
  // aliasName
  // handSomexh

  // 使用Reflect.get(target, key, receiver)
  // aliasName
  // name
  // handSomexh
</script> -->

<script type="module">
    import {reactive, effect} from "../js/reactivity";

    // reactive 让数据变成响应式
    // effect 副作用 数据变化后可以让effect重新执行 组件、watch、computed 都是基于effect实现
    const data = {
        name: "jw",
        age: 30,
        address: "zz",
        user: {
            post: "qd",
        },
        flag: false,
    };

    const state1 = reactive(data); // new Proxy
    /**
     * 1. 如果同一个对象，会返回同一个代理，在第一次利用weakMap把对象做了映射
     */
    const state2 = reactive(data);
    /**
     * state1 被代理过了 一定被增添了get和set
     */
    const state3 = reactive(state2);
    // console.log(state1 === state2)
    // console.log(state1 === state3)

    // effect(() => {
    //     app.innerHTML = state3.name + ',' + state3.age
    // })
    // state3.name // 放在外面的不进行依赖收集，执行完 effect 内的函数 activeEffect 重新置为 undefined
    // state3.name = 'jf'

    // effect(() => { // effect1
    //     state1.name
    // })

    /**
     * 2.如何保证内层 effect 执行完毕后，重新获取外层 effect （activeEffect）
     * 嵌套 effect，可以采用栈结构
     */

    /**
     * [effect1, effect2] 栈结构
     */
    // effect(() => { // effect1
    //     state1.name // 收集栈中的最后一个
    //     effect(() => { // effect2
    //         state1.age
    //     }) // 执行完毕后pop
    //     state1.address // ??
    // })

    /**
     * 嵌套effect，可以采用树结构
     */
    // activeEffect = null

    // effect(() => { // activeEffect = effect1
    //     state1.name
    //     effect(() => { // effect2.parent = activeEffect; activeEffect = effect1
    //         // activeEffect = effect2
    //         state1.age
    //     }) // activeEffect = effect2.parent
    //     state1.address // ??
    // })

    // effect(() => {
    //     app.innerHTML = state3.name + ',' + state3.age
    // })

    /**
     * 3. 更新属性每次都不一样导致死循环，activeEffect !== effect
     */

    // effect(() => {
    //     // 一直运行同一个effect
    //     state1.age = Math.random() // state1.age 改变后触发依赖更新，死循环
    //     app.innerHTML = state1.name
    // })

    /**
     * 4. 为什么要重新收集依赖? cleanupEffect(this)
     */
    // effect(() => {
    //     // flag， name
    //     // flag, age 清空上次收集，重新收集 取消掉name的收集  cleanupEffect(this)
    //     app.innerHTML = state3.flag ? state3.name : state3.age
    //     console.log('触发')
    // })
    // setTimeout(() => {
    //     state3.flag = false // 会显示 age
    //     setTimeout(() => {
    //         console.log('改了name，原则上不应该触发effect')
    //         state3.name = 'abc' // 需要更新吗？
    //     }, 1000)
    // }, 1000)

    /**
     * 5. 修改属性需要每次都更新吗？
     */

    let isFlushing = false;
    const runner = effect(
        () => {
            console.log("runner");
            // state3.user 如果这个也是对象，我们应该针对这个对象进行代理
            // 懒代理
            app.innerHTML = state3.user.post;
        },
        {
            //     调度执行
            scheduler() {
                // effect.run()
                if (!isFlushing) {
                    Promise.resolve().then(() => {
                        //     异步更新
                        runner();
                        isFlushing = false;
                    });
                    isFlushing = true;
                }
            },
        }
    );
    // state3.name = 1
    // state3.name = 2
    // state3.name = 3

    state3.user.post = "hd";
</script>
</body>
</html>
